function mov_tone( idlPath, idlFile, strPath, strFile )
%--------------------------------------------------------------------------
% Moving Spectral Average of Tone File
%
% Cooper Baker - 2014
%--------------------------------------------------------------------------

close all;

% Settings
%--------------------------------------------------------------------------
winSize = 512;
overlap = 4;
win     = chebwin( winSize, 125 );
name    = 'Moving Spectral Average';
headLoc = 0.25;
midLoc  = 0.5;
tailLoc = 0.75;
detail  = 0.02;

% Initializations
%--------------------------------------------------------------------------
if any( exist( 'idlFile' ) ~= 1 )
    [ idlFile, idlPath ] = uigetfile( '*.wav', 'Ideal Audio File' );
    [ strFile, strPath ] = uigetfile( '*.wav', 'Stretched Audio File' );
end

[ idlBuf, sr ] = audioread( [ idlPath, idlFile ] );
[ strBuf, sr ] = audioread( [ strPath, strFile ] );
idlSize        = length( idlBuf );
strSize        = length( strBuf );
idlMsec        = ( idlSize / sr ) * 1000;
rangeMsec      = detail * idlMsec;
headMsec       = ( headLoc - ( detail / 2 ) ) * idlMsec;
midMsec        = ( midLoc  - ( detail / 2 ) ) * idlMsec;
tailMsec       = ( tailLoc - ( detail / 2 ) ) * idlMsec;
headMsec       = headMsec + rangeMsec * 0.25;
midMsec        = midMsec  + rangeMsec * 0.25;
tailMsec       = tailMsec + rangeMsec * 0.25;
hopSize        = winSize / overlap;
hopMsec        = ( hopSize / sr ) * 1000;
gridMsec       = 500;
gridHops       = gridMsec / ( ( ( winSize / overlap ) / sr ) * 1000 );
halfWinSize    = winSize / 2;
hopMax         = length( idlBuf ) - winSize;
hop            = 1;
index          = 1;
winSum         = sum( win );

% crop or pad stretch buffer to match ideal buffer
if idlSize > strSize
    strBuf = [ strBuf ; zeros( idlSize - strSize, 1 ) ];
elseif strSize > idlSize
    strBuf = strBuf( 1 : length( idlBuf ) );
end

% Normalization
%--------------------------------------------------------------------------

% get file metadata
idlInfo = audioinfo( [ idlPath, idlFile ] );
strInfo = audioinfo( [ strPath, strFile ] );

% get file normalization coefficient
idlFileNorm = str2num( idlInfo.Title );
strFileNorm = str2num( strInfo.Title );

% de-normalize normalized .wav file data
idlRaw = idlBuf / idlFileNorm;
strRaw = strBuf / strFileNorm;

% make hann window
normWin = hann( round( idlSize / 2 ) );

% copy and window middle of files
idlNormWin = idlRaw( idlSize * 0.25 : idlSize * 0.75 - 1 ) .* normWin;
strNormWin = strRaw( idlSize * 0.25 : idlSize * 0.75 - 1 ) .* normWin;

% calculate rms of windowed file middles
idlRms = rms( idlNormWin );
strRms = rms( strNormWin );

% calculate normalization scalar
strScale = 1 / ( strRms / idlRms );

% scale stretch buffer to match ideal buffer
idlRaw = idlRaw;
strRaw = strRaw * strScale;

% Analysis Loop
%--------------------------------------------------------------------------
while hop < hopMax

    % copy analysis frame from buffer
    idlSamps = idlRaw( hop : hop + winSize - 1 );
    strSamps = strRaw( hop : hop + winSize - 1 );

    % window the frame
    idlWin = idlSamps .* win;
    strWin = strSamps .* win;

    % perform fft
    idlFullSpec = fft( idlWin );
    strFullSpec = fft( strWin );

    % save relevant halves of spectra
    idlSpec = idlFullSpec( 1 : halfWinSize );
    strSpec = strFullSpec( 1 : halfWinSize );

    % calculate magnitude spectra
    idlMagSpec = abs( idlSpec );
    strMagSpec = abs( strSpec );

    % calculate spectral magnitude averages
    idlAvg = sum( idlMagSpec ) / halfWinSize;
    strAvg = sum( strMagSpec ) / halfWinSize;

    % store average values into arrays
    idlMag( index ) = idlAvg;
    strMag( index ) = strAvg;

    % increment indices
    hop = hop + hopSize;
    index = index + 1;
end

% calculate amplitude spectra
idlAmp = ( idlMag / winSum );
strAmp = ( strMag / winSum );

% calculate sones spectra
idlSones = idlAmp .^ 0.6;
strSones = strAmp .^ 0.6;

% calculate decibel spectra
idlDb  = 20 * log10( idlAmp );
strDb  = 20 * log10( strAmp );

% clip -infinity to -120 decibels
idlDb( idlDb < -120 ) = -120;
strDb( strDb < -120 ) = -120;

% copy spectra to plot arrays
idlPlot = idlSones;
strPlot = strSones;

% calculate ideal plot offset
offset = 0;
offset = abs( min( [ min( idlPlot ) min( strPlot ) ] ) );

% offset plots
idlPlot = idlPlot + offset;
strPlot = strPlot + offset;

% calculate absolute difference between plots (error)
difPlot = abs( idlPlot - strPlot );

% calculate cropping values
side = round( index * detail );
head = round( index * headLoc );
mid  = round( index * midLoc );
tail = round( index * tailLoc );
range = side * 2;

% copy portions of spectra averges
idlHead = idlPlot( head - side : head + side );
idlMid  = idlPlot( mid  - side : mid  + side );
idlTail = idlPlot( tail - side : tail + side );
strHead = strPlot( head - side : head + side );
strMid  = strPlot( mid  - side : mid  + side );
strTail = strPlot( tail - side : tail + side );
difHead = difPlot( head - side : head + side );
difMid  = difPlot( mid  - side : mid  + side );
difTail = difPlot( tail - side : tail + side );

% calculate mean error value
difSig = difPlot( index * 0.25 : index * 0.75 );
difMean = sum( difSig ) / ( index * 0.5 );

% calculate max error value
difMax = max( difSig );

% Plots
%--------------------------------------------------------------------------
fontName   = 'Times New Roman';
fontSize   = 12;
xSpc       = 0.099;
ySpc       = 0.57;

fig = figure( 1 );
set( fig, 'Name', name );
set( fig, 'Position', [ 0 0 800 500 ] );
set( fig, 'defaultAxesFontName', fontName );
set( fig, 'defaultTextFontName', fontName );

% Main Plot
%--------------------------------------------------------------------------
set( subplot( 2, 1, 1 ), 'Position', [ 0.1, 0.5, 0.8, 0.3666 ] );
plot( idlPlot, 'Color', [ 0.2 0.2 0.8 ], 'LineWidth', 6 );
hold on;
plot( strPlot, 'Color', [ 0.2 0.8 0.2 ], 'LineWidth', 2 );
hold on;
plot( difPlot, 'Color', [ 0.8 0.2 0.2 ], 'LineWidth', 3 );
hold on;

% grid and labels
grid on
yMin = min( [ min( idlPlot ) min( strPlot ) min( difPlot ) ] );
yMax = max( [ max( idlPlot ) max( strPlot ) max( difPlot ) ] );
axis( [ 0 index yMin yMax ]);
set( gca, 'XTick', [ 0 : gridHops : index ] );
set( gca, 'XTickLabel', [ 0 : gridMsec : idlMsec ] );

% legend
xPos = ( index + index * -xSpc );
yPos = yMax * ySpc;
leg = legend( 'Ideal', 'Stretch', 'Error' );
set( leg, 'Location', 'NorthEast' );
maxStr = sprintf( ' Max: %2.5f', difMax );
meanStr = sprintf( ' Avg: %2.5f', difMean );
legStr = sprintf( '  Sones Error\n%s\n%s', maxStr, meanStr );
text( xPos, yPos, legStr, 'BackgroundColor', 'w', 'EdgeColor', 'k' );

% Head Plot
%--------------------------------------------------------------------------
set( subplot( 2, 3, 4 ), 'Position', [ 0.1, 0.1, 0.26, 0.3666 ] );

plot( idlHead, 'Color', [ 0.2 0.2 0.8 ], 'LineWidth', 6 );
hold on;
plot( strHead, 'Color', [ 0.2 0.8 0.2 ], 'LineWidth', 2 );
hold on;
plot( difHead, 'Color', [ 0.8 0.2 0.2 ], 'LineWidth', 3 );
hold on;

% grid and labels
grid on
yMin = min( [ min( idlPlot ) min( strPlot ) min( difPlot ) ] );
yMax = max( [ max( idlPlot ) max( strPlot ) max( difPlot ) ] );
axis( [ 0 range yMin yMax ]);
set( gca, 'XTick', [ range * 0.25 : ( range / 4 ) : range * 0.75 ] );
set( gca, 'XTickLabel', [ headMsec : rangeMsec / 4 : headMsec + rangeMsec ] );
yLab = ylabel( 'Sones' );
text( range * 0.03, yMax * 0.93, ' Head ', 'BackgroundColor', 'w', 'EdgeColor', 'k' );

% Mid Plot
%--------------------------------------------------------------------------
set( subplot( 2, 3, 5 ), 'Position', [ 0.3666, 0.1, 0.2666, 0.3666 ] );

plot( idlMid, 'Color', [ 0.2 0.2 0.8 ], 'LineWidth', 6 );
hold on;
plot( strMid, 'Color', [ 0.2 0.8 0.2 ], 'LineWidth', 2 );
hold on;
plot( difMid, 'Color', [ 0.8 0.2 0.2 ], 'LineWidth', 3 );
hold on;

% grid and labels
grid on
yMin = min( [ min( idlPlot ) min( strPlot ) min( difPlot ) ] );
yMax = max( [ max( idlPlot ) max( strPlot ) max( difPlot ) ] );
axis( [ 0 range yMin yMax ]);
set( gca, 'XTick', [ range * 0.25 : ( range / 4 ) : range * 0.75 ] );
set( gca, 'XTickLabel', [ midMsec : rangeMsec / 4 : midMsec + rangeMsec ] );
set( gca, 'YTickLabel', [] );
xlabel( 'Milliseconds' );
text( range * 0.5, yMax * 0.93, ' Middle ', 'BackgroundColor', 'w', 'EdgeColor', 'k', 'HorizontalAlignment', 'center' );

% Tail Plot
%--------------------------------------------------------------------------
set( subplot( 2, 3, 6 ), 'Position', [ 0.6399, 0.1, 0.26, 0.3666 ] );

plot( idlTail, 'Color', [ 0.2 0.2 0.8 ], 'LineWidth', 6 );
hold on;
plot( strTail, 'Color', [ 0.2 0.8 0.2 ], 'LineWidth', 2 );
hold on;
plot( difTail, 'Color', [ 0.8 0.2 0.2 ], 'LineWidth', 3 );
hold on;

% grid and labels
grid on
yMin = min( [ min( idlPlot ) min( strPlot ) min( difPlot ) ] );
yMax = max( [ max( idlPlot ) max( strPlot ) max( difPlot ) ] );
axis( [ 0 range yMin yMax ]);
set( gca, 'XTick', [ range * 0.25 : ( range / 4 ) : range * 0.75 ] );
set( gca, 'XTickLabel', [ tailMsec : rangeMsec / 4 : tailMsec + rangeMsec ] );
set( gca, 'YTickLabel', [] );
text( range * 0.97, yMax * 0.93, ' Tail ', 'BackgroundColor', 'w', 'EdgeColor', 'k', 'HorizontalAlignment', 'right' );

% tighten up figure borders
tightfig();

set( yLab, 'Position', [ range * -0.13, ( yMax * 1.05 ) ] );

% Save Data to Files
%--------------------------------------------------------------------------

% get file name
[ a, fileName, b ] = fileparts( strFile );

% export graph
hgexport( fig, [ fileName, '.mov.eps' ] );

% write error data to files
csvFile = sprintf( '%s.mov.err.csv', fileName );
csvwrite( csvFile, difPlot );

% EOF
